#ifndef __UTILS_H__
#define __UTILS_H__

// #define _OPEN

#include <cstdio>
#include <cstdlib>
#include <string>
#include <cassert>
#include <omp.h>
#include <cmath>
#include <sys/stat.h> // struct stat
#include <time.h>
#include <chrono>
#ifdef _OPEN
#include "cblas.h"
#include "lapack.h"
#else
#include "mkl.h"
#endif
#ifdef _WIN64
#include <windows.h>
#endif
#include "common.h"
#include <vector>

size_t getFileSize(const char* fileName);
void read_Fortran_unformatted(std::string filename, int& nrows, int& ncols, double*& vals);
void write_Fortran_unformatted(std::string filename, const int nrows, const int ncols, const double* vals);
void check_sum(const int len, const double* data, const int interval);

extern int gnt;

template<typename data_t1, typename data_t2>
void copy_vec(const size_t nrows, const data_t1* src, data_t2* dst) {
    #pragma omp parallel for schedule(static) num_threads(gnt)
    for (size_t i = 0; i < nrows; i++)
        dst[i] = src[i];
}

template<typename data_t>
void zero_vec(const size_t nrows, data_t* vec) {
    #pragma omp parallel for schedule(static) num_threads(gnt)
    for (size_t i = 0; i < nrows; i++)
        vec[i] = 0.0;
}

template<typename data_t1, typename data_t2>
void copy_mat(const data_t1* src, int src_lda, data_t2* dst, int dst_lda, int num_cols, int num_rows) {
    #pragma omp parallel for schedule(static) num_threads(gnt)
    for (int j = 0; j < num_cols; j++)
    for (int i = 0; i < num_rows; i++) {
        dst[j * dst_lda + i] = src[j * src_lda + i];
    }
}


template<typename data_t>
void scalar_print(const data_t * src, const int len)
{
    for (int i = 0; i < len; i++)
        printf("%.5e ", src[i]);
    printf("\n");
}

template<typename data_t>
bool check_correctness(const int nrows, const data_t* A, const data_t* sol, const data_t* rhs,
    const double tol = 1e-12)
{
    static_assert(sizeof(data_t) == 8);// double to check
    double* resi = new double [nrows];
    copy_vec(nrows, rhs, resi);// resi := b
    cblas_dgemv(CblasColMajor, CblasNoTrans, nrows, nrows, -1.0, A, nrows, sol, 1, 1.0, resi, 1);// resi = b - A*x

    double resi_nrm2 = cblas_dnrm2(nrows, resi, 1);
    double rhs_nrm2  = cblas_dnrm2(nrows, rhs , 1);
    double ratio = resi_nrm2 / rhs_nrm2;
    printf("||b-A*x||/||b|| = %.10e\n", ratio);

    delete[] resi;
    if (ratio > tol) return false;
    else return true;
}

void stream_test(int Nelem);
void setProcessRealTime();

#endif